package main

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"os"
	"runtime"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
	MODBUS "github.com/goburrow/modbus"
)

// 定义 采集的数据结构
var realtime RawRealtime
var settings RawMcuSettings
var modbusClient *ModbusClient

// ModbusClient is used for connecting the device and read/write value
type ModbusClient struct {
	// IsModbusTcp is a value indicating the connection type
	IsModbusTcp bool
	// TCPClientHandler is ued for holding device TCP connection
	TCPClientHandler MODBUS.TCPClientHandler
	// TCPClientHandler is ued for holding device RTU connection
	RTUClientHandler MODBUS.RTUClientHandler

	client MODBUS.Client

	// 可配置的 机台配置参数
	configurableModbusRegisters map[string]uint16
}

func (c *ModbusClient) OpenConnection() {
	var err error
	var newClient MODBUS.Client
	if c.IsModbusTcp {
		err = c.TCPClientHandler.Connect()
		newClient = MODBUS.NewClient(&c.TCPClientHandler)
		fmt.Println("Modbus client create TCP connection.")
	} else {
		err = c.RTUClientHandler.Connect()
		newClient = MODBUS.NewClient(&c.RTUClientHandler)
		fmt.Println("Modbus client create RTU connection.")
	}
	c.client = newClient

	if err != nil {
		fmt.Println(err)
	}

}

func (c *ModbusClient) CloseConnection() error {
	return c.RTUClientHandler.Close()
}

func onCommandReceivedFromBroker(client mqtt.Client, message mqtt.Message) {

	payload := message.Payload()
	fmt.Printf("### onCommandReceivedFromBroker payload: \n %v ###\n", payload)

	// 1 解析消息
	var reqParametersJsonBody ReqParametersJsonBody
	err := json.Unmarshal(message.Payload(), &reqParametersJsonBody)
	if err != nil {
		fmt.Printf("onCommandReceivedFromBroker - Error unmarshaling payload: %s\n", err)
		return
	}

	var param string
	var address, value uint16
	var ok bool

	param_count := len(reqParametersJsonBody.Parameters)

	// 2 向机台 写入值
	for idx := 0; idx < param_count; idx++ {
		// 写 modbus 寄存器
		param = reqParametersJsonBody.Parameters[idx].Param
		value = reqParametersJsonBody.Parameters[idx].Value
		//address = modbusClient.configurableModbusRegisters[param]
		if address, ok = modbusClient.configurableModbusRegisters[param]; !ok {
			fmt.Printf("### modbusClient.configurableModbusRegisters invalid param: %s ###\n", param)
			continue
		}
		modbusClient.client.WriteSingleRegister(address, value)
	}

	// 3 重新读取出来
	//var response []byte
	//var err error
	for idx := 0; idx < param_count; idx++ {
		// 写 modbus 寄存器
		param = reqParametersJsonBody.Parameters[idx].Param
		value = reqParametersJsonBody.Parameters[idx].Value
		//address = modbusClient.configurableModbusRegisters[param]
		if address, ok = modbusClient.configurableModbusRegisters[param]; !ok {
			fmt.Printf("### modbusClient.configurableModbusRegisters invalid param: %s ###\n", param)
			continue
		}
		response, err := modbusClient.client.ReadHoldingRegisters(address, 1)
		if err != nil {
			continue
		}

		reqParametersJsonBody.Parameters[idx].Value = uint16(response[0])*256 + uint16(response[1])
	}

	// 4 发送给上层
	reqParametersJsonBody.Timestamp = uint64(time.Now().Unix())
	data, err := json.Marshal(reqParametersJsonBody)
	if err != nil {
		return
	}
	client.Publish(commandRespTopic, 1, false, data)

}

func main() {
	modbusClient = new(ModbusClient)
	modbusClient.IsModbusTcp = false

	sysType := runtime.GOOS
	if sysType == "windows" {
		modbusClient.RTUClientHandler.Address = "COM4"
	} else {
		modbusClient.RTUClientHandler.Address = "/dev/ttyUSB0"
	}
	fmt.Printf("### sysType:%s, modbusClient.RTUClientHandler.Address %s ###\n", sysType, modbusClient.RTUClientHandler.Address)

	modbusClient.RTUClientHandler.SlaveId = byte(1)
	modbusClient.RTUClientHandler.Timeout = time.Duration(1) * time.Second
	modbusClient.RTUClientHandler.IdleTimeout = time.Duration(3) * time.Second
	modbusClient.RTUClientHandler.BaudRate = 115200
	modbusClient.RTUClientHandler.DataBits = 8
	modbusClient.RTUClientHandler.StopBits = 1
	modbusClient.RTUClientHandler.Parity = "N"
	modbusClient.RTUClientHandler.Logger = log.New(os.Stdout, "", log.LstdFlags)

	modbusClient.InitialRegisters()
	modbusClient.OpenConnection()

	var response []byte
	var mqttClient mqtt.Client
	mqttClient, _ = initialMqttClient()

	defer mqttClient.Disconnect(5000)
	token := mqttClient.Subscribe(commandReqTopic, byte(0), onCommandReceivedFromBroker)
	fmt.Printf("### mqttClient.Subscribe: %s ###\n", commandReqTopic)
	if token.Wait() && token.Error() != nil {
		fmt.Println(token.Error())
	}

	var times = 0
	for {
		times += 1
		fmt.Printf("\n### modbusClient.Read Spindles Begin ###\n")
		// 读取 断头寄存器
		response = modbusClient.ReadModbusData(1000, 120)
		token1 := mqttClient.Publish(brokensTopic, 1, false, response)
		if token1.Wait() && token1.Error() != nil {
			fmt.Println(token1.Error())
		}
		// 读取 弱捻寄存器
		response = modbusClient.ReadModbusData(1150, 120)
		token2 := mqttClient.Publish(weaksTopic, 1, false, response)
		if token2.Wait() && token2.Error() != nil {
			fmt.Println(token2.Error())
		}

		// 读取 空置寄存器
		response = modbusClient.ReadModbusData(1300, 120)
		token3 := mqttClient.Publish(noocupationsTopic, 1, false, response)
		if token3.Wait() && token3.Error() != nil {
			fmt.Println(token3.Error())
		}

		fmt.Printf("\n### modbusClient.Read Realtime Begin ###\n")
		// 读取 机台实时产量 寄存器
		_ = modbusClient.ReadModbusDataRealtime(&realtime, &settings)

		buf := bytes.Buffer{}
		err2 := binary.Write(&buf, binary.LittleEndian, realtime)
		if err2 != nil {
			fmt.Printf("### Realtime binary.Write error: %v ###\n", err2.Error())
			continue
		}
		raw_bytes := buf.Bytes()

		token4 := mqttClient.Publish(realtimeTopic, 1, false, raw_bytes)
		if token4.Wait() && token4.Error() != nil {
			fmt.Println(token4.Error())
		}

		// 每隔 60秒发一次系统配置参数
		if times%60*10 == 0 {
			fmt.Printf("\n### modbusClient.Read Setting Begin ###\n")
			// 读取 机台配置 寄存器 settingTopic
			modbusClient.ReadModbusDataSettings(&settings)

			buf2 := bytes.Buffer{}
			err3 := binary.Write(&buf2, binary.LittleEndian, settings)
			if err3 != nil {
				fmt.Printf("### Settings binary.Write error: %v ###\n", err3.Error())
				continue
			}
			raw_bytes2 := buf2.Bytes()
			fmt.Printf("### Settings binary.Write raw_bytes2: %v ###\n", raw_bytes2)

			token5 := mqttClient.Publish(settingTopic, 1, false, raw_bytes2)
			if token5.Wait() && token5.Error() != nil {
				fmt.Println(token5.Error())
			}
		}
		time.Sleep(time.Second * 1)
	}
}

func (c *ModbusClient) ReadModbusData(startAddress uint16, len uint16) []byte {
	var response []byte
	var err error

	fmt.Printf("### Modbus client ReadModbusData Begin ###\n")

	response, err = c.client.ReadHoldingRegisters(startAddress, len)
	if err != nil {
		return nil
	}

	//fmt.Printf("### Modbus client ReadModbusData's results %v ###\n", response)
	return response
}

func (c *ModbusClient) ReadModbusDataRealtime(realtime *RawRealtime, settings *RawMcuSettings) error {
	var response []byte
	var err error
	//var realtime RawRealtime

	fmt.Printf("### Modbus client ReadModbusDataRealtime Begin ###\n")

	response, err = c.client.ReadHoldingRegisters(8006, 101)
	fmt.Printf("### Modbus client ReadModbusDataRealtime's Register8006: %v ###\n", response)
	if err != nil || len(response) < 101*2 {
		fmt.Println(err)
		return nil
	}

	realtime.Spin_len = uint64(response[0])*256 + uint64(response[1])
	realtime.Front_speed = uint16(response[1*2])*256 + uint16(response[1*2+1])
	realtime.Avg_speed = uint16(response[71*2])*256 + uint16(response[71*2+1])

	realtime.BrokensL_num = uint16(response[5*2])*256 + uint16(response[5*2+1])
	realtime.WeaksL_num = uint16(response[6*2])*256 + uint16(response[6*2+1])
	realtime.BrokensR_num = uint16(response[8*2])*256 + uint16(response[8*2+1])
	realtime.WeaksR_num = uint16(response[9*2])*256 + uint16(response[9*2+1])

	realtime.NoocupationsL_num = uint16(response[88*2])*256 + uint16(response[88*2+1])
	realtime.NoocupationsR_num = uint16(response[89*2])*256 + uint16(response[89*2+1])
	realtime.MalfunctionsL_num = uint16(response[90*2])*256 + uint16(response[90*2+1])
	realtime.MalfunctionsR_num = uint16(response[91*2])*256 + uint16(response[91*2+1])

	realtime.Theory_len = uint64(response[98*2])*256 + uint64(response[98*2+1])
	realtime.Actual_len = uint64(response[99*2])*256 + uint64(response[99*2+1])

	settings.Roving_cut = uint16(response[17*2])*256 + uint16(response[17*2+1])
	settings.Spindle_count = uint16(response[100*2])*256 + uint16(response[100*2+1])
	settings.FRoller_Diameter = uint16(response[95*2])*256 + uint16(response[95*2+1])
	settings.Theory_Spinlen = uint16(response[96*2])*256 + uint16(response[96*2+1])

	// 采集机台状态
	response, err = c.client.ReadHoldingRegisters(7, 1)
	fmt.Printf("### Modbus client ReadModbusData Realtime's Register7: %v ###\n", response)

	if err == nil && len(response) > 0 {
		realtime.Status = uint16(response[1])
	}

	fmt.Printf("### Modbus client ReadModbusData Realtime's realtime: %v ###\n", realtime)
	fmt.Printf("### Modbus client ReadModbusData Realtime's settings: %v ###\n", settings)

	return nil
}

func (c *ModbusClient) ReadModbusDataVersion(settings *RawMcuSettings) {
	var response []byte
	var err error

	fmt.Printf("### Modbus client ReadModbusDataVersion Begin ###\n")

	response, err = c.client.ReadHoldingRegisters(9001, 2)
	if err != nil {
		return
	}

	fmt.Printf("### Modbus client ReadModbusDataVersion's results %v ###\n", response)
	settings.Ver_main = uint16(response[0]) + uint16(response[1])*256
	settings.Ver_sub = uint16(response[2]) + uint16(response[3])*256
}

func (c *ModbusClient) ReadModbusDataSettings(settings *RawMcuSettings) {
	var response []byte
	var err error

	fmt.Printf("### Modbus client ReadModbusDataSettings Begin ###\n")

	response, err = c.client.ReadHoldingRegisters(8200, 25)
	if err != nil {
		fmt.Printf("### ReadModbusDataSettings ReadHoldingRegisters 8200 err: %s ###\n", err)
		return
	}
	settings.Boot_delay = uint16(response[0])*256 + uint16(response[1])
	settings.Standard_front_speed = uint16(response[2*2])*256 + uint16(response[2*2+1])
	settings.Max_stop_feed = uint16(response[3*2])*256 + uint16(response[3*2+1])
	settings.Min_stop_feed = uint16(response[6*2])*256 + uint16(response[6*2+1])
	settings.Boot_speed = uint16(response[24*2])*256 + uint16(response[24*2+1])

	response, err = c.client.ReadHoldingRegisters(8300, 97)
	if err != nil {
		fmt.Printf("### ReadModbusDataSettings ReadHoldingRegisters 8300 err: %s ###\n", err)
		return
	}
	settings.Broken_threshold_speed = uint16(response[0*2])*256 + uint16(response[0*2+1])
	settings.Broken_threshold_duration = uint16(response[1*2])*256 + uint16(response[1*2+1])
	settings.Fix_threshold_speed = uint16(response[2*2])*256 + uint16(response[2*2+1])
	settings.Fix_threshold_duration = uint16(response[3*2])*256 + uint16(response[3*2+1])
	settings.Fix_delay = uint16(response[4*2])*256 + uint16(response[4*2+1])

	settings.Weak_speed_ratio = uint16(response[5*2])*256 + uint16(response[5*2+1])
	settings.Weak_threshold_duration = uint16(response[6*2])*256 + uint16(response[6*2+1])
	settings.Nooc_threshold_duration = uint16(response[9*2])*256 + uint16(response[9*2+1])
	settings.Malfunc_Cond = uint16(response[10*2])*256 + uint16(response[10*2+1])

	settings.Probe_cover = uint16(response[77*2])*256 + uint16(response[77*2+1])
	settings.SignV = uint16(response[7*2])*256 + uint16(response[7*2+1])
	settings.PrecheckTime = uint16(response[76*2])*256 + uint16(response[76*2+1])
	settings.InductionIntensity = uint16(response[81*2])*256 + uint16(response[81*2+1])
	settings.LuminousIntensity = uint16(response[82*2])*256 + uint16(response[82*2+1])

	response, err = c.client.ReadHoldingRegisters(8500, 14)
	if err != nil {
		fmt.Printf("### ReadModbusDataSettings ReadHoldingRegisters 8500 err: %s ###\n", err)
		return
	}
	settings.Alloc_address = uint16(response[0*2])*256 + uint16(response[0*2+1])
	settings.Spin_Dev_count = uint16(response[4*2])*256 + uint16(response[4*2+1])
	settings.Mannual_cut_l = uint16(response[5*2])*256 + uint16(response[5*2+1])
	settings.Mannual_cut_h = uint16(response[6*2])*256 + uint16(response[6*2+1])
	settings.Mannual_cut = uint16(response[7*2])*256 + uint16(response[7*2+1])
	settings.Cut_Dev_count = uint16(response[13*2])*256 + uint16(response[13*2+1])

	response, err = c.client.ReadHoldingRegisters(8600, 4)
	if err != nil {
		fmt.Printf("### ReadModbusDataSettings ReadHoldingRegisters 8600 err: %s ###\n", err)
		return
	}
	settings.Spin_Raddress = uint16(response[0*2])*256 + uint16(response[0*2+1])
	settings.Spin_Laddress = uint16(response[1*2])*256 + uint16(response[1*2+1])
	settings.Cut_Raddress = uint16(response[2*2])*256 + uint16(response[2*2+1])
	settings.Cut_Laddress = uint16(response[3*2])*256 + uint16(response[3*2+1])

	fmt.Printf("### Modbus client ReadModbusDataSettings's settings %v ###\n", settings)
}

func initialMqttClient() (mqtt.Client, error) {

	uri := &url.URL{
		Scheme: "tcp",
		Host:   fmt.Sprintf("%s:%d", brokerUrl, brokerPort),
		User:   url.UserPassword(username, password),
	}

	clientID := "spinning-modbus-rtu-client"
	fmt.Printf("### Create MQTT client and connection: uri=%v clientID=%v ###\n", uri.String(), clientID)
	opts := mqtt.NewClientOptions()
	opts.AddBroker(fmt.Sprintf("%s://%s", uri.Scheme, uri.Host))
	opts.SetClientID(clientID)
	opts.SetUsername(uri.User.Username())
	password, _ := uri.User.Password()
	opts.SetPassword(password)

	opts.SetConnectionLostHandler(func(client mqtt.Client, e error) {
		fmt.Printf("Connection lost : %v", e)
		token := client.Connect()
		if token.Wait() && token.Error() != nil {
			fmt.Printf("Reconnection failed : %v", e)
		} else {
			fmt.Printf("Reconnection sucessful : %v", e)
		}
	})

	mqttClient := mqtt.NewClient(opts)
	fmt.Printf("### createMqttClient Begin ###\n")
	token := mqttClient.Connect()
	if token.Wait() && token.Error() != nil {
		fmt.Printf("### mqttClient.Connect failed: %s ###", token.Error())
		return nil, token.Error()
	}
	fmt.Printf("### createMqttClient Success ###\n")
	return mqttClient, nil
}
